/**
 * Test Class - ReportsTests.
 * Boundary Conditions (Low, Middle, High, Null, 
 * Empty, Valid, Correct, Duplicates and more).
 * Tests use methods such as assertEquals, assertTrue, assertFalse.
 * 
 * @author ct267, msm41, sg350.
 */
package tests;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;
import org.junit.After;

import restaurant.CategoryDoseNotExist;
import restaurant.IOsystem;
import restaurant.MenuItem;
import restaurant.OrderItem;
import restaurant.Reports;

import java.util.ArrayList;
import java.util.TreeSet;

public class ReportsTests {

	/** Test environment variables **/
	private Reports reports = null;
	private IOsystem ioSystem = null;
	private TreeSet<MenuItem> menuSet = null;
	private ArrayList<OrderItem> orderList = null;

	/** Setup environment model before running tests **/
	@Before
	public void testSetup() {
		ioSystem = new IOsystem();
		menuSet = new TreeSet<MenuItem>(ioSystem.readMenuFile("menu_input.txt"));
		orderList = new ArrayList<OrderItem>(
				ioSystem.readOrderFile("order_input.txt"));
		reports = new Reports(orderList, menuSet);
	}

	/** Shutdown environment model and clean up resources **/
	@After
	public void tareDown() {
		orderList.clear();
		menuSet.clear();
		ioSystem = null;
		menuSet = null;
		reports = null;
	}

	/**
	 * Multiple sequential tests relevant to the menu TreeSet. Tests include
	 * checking for empty set, Single object check for one item and multiple
	 * items. Also checks to see if duplicates have been rejected. Tests are
	 * also included which pass mock menu item variables to the method, then
	 * returned data is checked (asserted).
	 * 
	 **/
	@Test
	public void testMenuSet() throws CategoryDoseNotExist {

		// Check TreeSet is not initialised empty.
		assertFalse(menuSet.isEmpty());

		// Check TreeSet empty.
		menuSet.clear();
		assertTrue(menuSet.isEmpty());
		assertEquals(0, menuSet.size());

		/*
		 * (Empty) Check list size is 0 and values with "" item. Checks to see
		 * if the correct exception is caught.
		 */
		try {
			menuSet.clear();
			String expectedObject = "";
			menuSet.add(new MenuItem("", 0, ""));
			assertEquals(expectedObject, menuSet.first().getDish()
					+ menuSet.first().getCost() + menuSet.first().getCategory());
		} catch (Exception e) {
			assertEquals(e.getMessage(), ("Empty Value"));
			assertEquals(0, menuSet.size()); // Check correct set size.
		}

		// (Low Bound) Check set size and values with 1 item.
		menuSet.clear();
		String expectedObject1 = "Chicken1.0Main";
		menuSet.add(new MenuItem("Chicken", 1, "Main"));
		assertEquals(1, menuSet.size()); // Check correct set size.
		assertEquals(expectedObject1, menuSet.first().getDish()
				+ menuSet.first().getCost() + menuSet.first().getCategory());

		// (High Bound) Check set size and values with 1> items.
		String expectedObject2 = "IceCream2.0Dessert";
		menuSet.add(new MenuItem("IceCream", 2, "Dessert"));
		assertEquals(2, menuSet.size());
		assertEquals(expectedObject2, menuSet.last().getDish()
				+ menuSet.last().getCost() + menuSet.last().getCategory());

		/*
		 * (Duplicates) Check that no duplicate objects can be added and
		 * expected1 as first and expected 2 as the last object remain from
		 * previous checks.
		 */
		menuSet.add(new MenuItem("Chicken", 2, "Main"));
		assertEquals(2, menuSet.size());
	}

	/**
	 * Multiple sequential tests relevant to the order list ArrayList Tests
	 * include checking for empty list, low index check for one item and
	 * multiple items. Tests are also included which pass mock order variables
	 * to the method and returned data is checked (asserted).
	 **/
	@Test
	public void testOrderList() {

		// Check ArrayList not initialised empty.
		assertFalse(orderList.isEmpty());

		// Check ArrayList empty.
		orderList.clear();
		assertTrue(orderList.isEmpty());
		assertEquals(0, orderList.size());

		// (Low Bound) Check list size and values with 1 item.
		String expectedObject = "1test1";
		orderList.add(new OrderItem(1, "test", 1));
		assertEquals(1, orderList.size());
		assertEquals(expectedObject, orderList.get(0).getTableid() + ""
				+ orderList.get(0).getName() + ""
				+ orderList.get(0).getQuantity());
		
		// (High Bound) Check list size and values with 1> items.
		String expectedObject2 = "2test2";
		orderList.add(new OrderItem(2, "test", 2));
		assertEquals(2, orderList.size());
		assertEquals(expectedObject2, orderList.get(1).getTableid() + ""
				+ orderList.get(1).getName() + ""
				+ orderList.get(1).getQuantity());
		
	}

	/**
	 * Multiple tests relevant to removeNonAlpha method ensuring that only
	 * alphabetical characters are returned. Any non alpha characters will show
	 * a test error.
	 **/
	@Test
	public void removeNonAlpha() throws Exception {
		// Alpha String.
		String expected1 = "AbCdEaBcDe";
		String supplied1 = "AbCdEaBcDe";
		String error1 = "Failed for AbCdEaBcDe\n";
		String actual1 = reports.removeNonAlpha(supplied1);
		assertEquals(error1, expected1, actual1);

		// Empty String.
		String expected2 = "";
		String supplied2 = "";
		String error2 = "Failed for empty string\n";
		String actual2 = reports.removeNonAlpha(supplied2);
		assertEquals(error2, expected2, actual2);

		// Mixed String with alpha, numbers, symbols.
		String expected3 = "AbCdE";
		String supplied3 = "A!1b£2C%3d4(*^E5";
		String error3 = "Failed for mixed string\n";
		String actual3 = reports.removeNonAlpha(supplied3);
		assertEquals(error3, expected3, actual3);
	}

	/**
	 * Multiple tests relevant to checking the consistency for frequency data
	 * and report population. Ensuring that the report is not generated with
	 * empty string and the report contains the expected value.
	 **/
	@Test
	public void getFrequecyReport() {
		// Not empty report.
		assertFalse(reports.getFrequecyReport().isEmpty());

		// Check for valid output value.
		String expected1 = "CHICKEN";
		assertTrue(reports.getFrequecyReport().contains(expected1));
	}

	/**
	 * Multiple tests relevant to check data and consistency for finding the
	 * dishes that are not ordered.
	 * 
	 * Use of assertTrue & assertFalse checks. (Assumes that what you are
	 * testing for is true or false and error is shown if this is not the case.
	 * 
	 * **/
	@Test
	public void getDishNotOrdered() {
		// Not empty report.
		assertFalse(reports.getDishesNotOrdered().isEmpty());

		// Check beef not ordered.
		assertTrue(reports.getDishesNotOrdered().contains("BEEF"));

		// Check garlic bread not valid.
		assertTrue(reports.getDishesNotOrdered().contains("GARLIC BREAD"));

	}

}
